//
//	File: D3D9Camera.cpp
//

#include "D3D9Camera.h"

void OyD3D9Camera::SetViewMatrix3D(	const OyVector4D& vcRight, const OyVector4D& vcUp, 
									const OyVector4D& vcDir, const OyVector4D& vcEyePos)
{
	m_CameraPos = vcEyePos;
	m_mViewMatrix._14 = m_mViewMatrix._24 = m_mViewMatrix._34 = 0.0f;
	m_mViewMatrix._44 = 1.0f;

	m_mViewMatrix._11 = vcRight.x;
	m_mViewMatrix._21 = vcRight.y;
	m_mViewMatrix._31 = vcRight.z;
	m_mViewMatrix._41 = - (vcRight*vcEyePos);

	m_mViewMatrix._12 = vcUp.x;
	m_mViewMatrix._22 = vcUp.y;
	m_mViewMatrix._32 = vcUp.z;
	m_mViewMatrix._42 = - (vcUp*vcEyePos);

	m_mViewMatrix._13 = vcDir.x;
	m_mViewMatrix._23 = vcDir.y;
	m_mViewMatrix._33 = vcDir.z;
	m_mViewMatrix._43 = - (vcDir*vcEyePos);

	CalcViewProjMatrix();
}

void OyD3D9Camera::SetClippingPlanes(float fNear, float fFar)
{
	m_fNear = fNear;
	m_fFar  = fFar;

	if(m_fNear <= 0.0f)
		m_fNear = 0.01f;

	if(m_fFar <= 1.0f)
		m_fFar = 1.00f;
   
	if(m_fNear >= m_fFar)
	{
		m_fNear = m_fFar;
		m_fFar  = m_fNear + 1.0f;
	}

	// change orthogonal projection
	float Q = 1.0f / (m_fFar - m_fNear);
	float X = m_fNear / (m_fNear - m_fFar);
	if( m_Mode == EMD_TWOD )
	{
		// change 2D projection and view
		Prepare2DMode();
	}
	else if( m_Mode == EMD_ORTHOGONAL )
	{
		m_mProjMatrix._33 = Q;
		m_mProjMatrix._43 = X;
	}
	else if( m_Mode == EMD_PERSPECTIVE )
	{
		Q *= m_fFar;
		X = -Q * m_fNear;
		m_mProjMatrix._33 = Q;
		m_mProjMatrix._43 = X;
	}
}

/**
 * Calculate orthogonal projection and view matrix to render
 * vertices who's world-coordinates are given in screen space. (2D MODE)
 */
void OyD3D9Camera::Prepare2DMode(void) 
{
	// set matrices to identity
	memset(&m_mViewMatrix, 0, sizeof(OyMatrix)); 
	memset(&m_mProjMatrix, 0, sizeof(OyMatrix)); 
	m_mViewMatrix._11 = m_mViewMatrix._33 = m_mViewMatrix._44 = 1.0f;

	// orthogonal projection matrix
	m_mProjMatrix._11 =  2.0f/(float)m_ViewPort.Width;
	m_mProjMatrix._22 =  2.0f/(float)m_ViewPort.Height;
	m_mProjMatrix._33 =  1.0f/(m_fFar-m_fNear);
	m_mProjMatrix._43 = -m_fNear*(1.0f/(m_fFar-m_fNear));
	m_mProjMatrix._44 =  1.0f;

	// 2D view matrix
	float tx, ty, tz;
	tx = -((int)m_ViewPort.Width) + m_ViewPort.Width * 0.5f;
	ty =  m_ViewPort.Height - m_ViewPort.Height * 0.5f;
	tz =  m_fNear + 0.1f;

	m_mViewMatrix._22 = -1.0f;
	m_mViewMatrix._41 = tx; 
	m_mViewMatrix._42 = ty; 
	m_mViewMatrix._43 = tz;

	CalcViewProjMatrix();
}

/**
 * Retrieve active frustrum planes, normals pointing outwards.
 * -> IN: OyPlane* - address to store 6 planes
 */
void OyD3D9Camera::GetFrustrum(OyPlane *p)
{
	// left plane
	p[0].m_vcNormal.x = -(m_mViewProjMatrix._14 + m_mViewProjMatrix._11);
	p[0].m_vcNormal.y = -(m_mViewProjMatrix._24 + m_mViewProjMatrix._21);
	p[0].m_vcNormal.z = -(m_mViewProjMatrix._34 + m_mViewProjMatrix._31);
	p[0].m_fDistance  = -(m_mViewProjMatrix._44 + m_mViewProjMatrix._41);

	// right plane
	p[1].m_vcNormal.x = -(m_mViewProjMatrix._14 - m_mViewProjMatrix._11);
	p[1].m_vcNormal.y = -(m_mViewProjMatrix._24 - m_mViewProjMatrix._21);
	p[1].m_vcNormal.z = -(m_mViewProjMatrix._34 - m_mViewProjMatrix._31);
	p[1].m_fDistance  = -(m_mViewProjMatrix._44 - m_mViewProjMatrix._41);

	// top plane
	p[2].m_vcNormal.x = -(m_mViewProjMatrix._14 - m_mViewProjMatrix._12);
	p[2].m_vcNormal.y = -(m_mViewProjMatrix._24 - m_mViewProjMatrix._22);
	p[2].m_vcNormal.z = -(m_mViewProjMatrix._34 - m_mViewProjMatrix._32);
	p[2].m_fDistance  = -(m_mViewProjMatrix._44 - m_mViewProjMatrix._42);
   
	// bottom plane
	p[3].m_vcNormal.x = -(m_mViewProjMatrix._14 + m_mViewProjMatrix._12);
	p[3].m_vcNormal.y = -(m_mViewProjMatrix._24 + m_mViewProjMatrix._22);
	p[3].m_vcNormal.z = -(m_mViewProjMatrix._34 + m_mViewProjMatrix._32);
	p[3].m_fDistance  = -(m_mViewProjMatrix._44 + m_mViewProjMatrix._42);

	// near plane
	p[4].m_vcNormal.x = -m_mViewProjMatrix._13;
	p[4].m_vcNormal.y = -m_mViewProjMatrix._23;
	p[4].m_vcNormal.z = -m_mViewProjMatrix._33;
	p[4].m_fDistance  = -m_mViewProjMatrix._43;

	// far plane
	p[5].m_vcNormal.x = -(m_mViewProjMatrix._14 - m_mViewProjMatrix._13);
	p[5].m_vcNormal.y = -(m_mViewProjMatrix._24 - m_mViewProjMatrix._23);
	p[5].m_vcNormal.z = -(m_mViewProjMatrix._34 - m_mViewProjMatrix._33);
	p[5].m_fDistance  = -(m_mViewProjMatrix._44 - m_mViewProjMatrix._43);

	// normalize frustrum normals
	for(int i=0;i<6;i++)
	{
		float fL = p[i].m_vcNormal.GetLength();
		p[i].m_vcNormal /= fL;
		p[i].m_fDistance  /= fL;
	}
}

/**
 * Calculate perspective 3D projection matrix.
 * -> IN: float     - field of horizontal view
 *        float     - aspect ration (viewport height / width)
 */
HRESULT OyD3D9Camera::CalcPerspProjMatrix(float fFOV, float fAspect)
{
	if(fabs(m_fFar - m_fNear) < 0.01f)
		return OY_FAIL;

	float sinFOV2 = sinf(fFOV/2);

	if(fabs(sinFOV2) < 0.01f)
		return OY_FAIL;

	float cosFOV2 = cosf(fFOV/2);

	float w = (cosFOV2 / sinFOV2) / fAspect;
	float h = 1.0f  * (cosFOV2 / sinFOV2);
	float Q = m_fFar / (m_fFar - m_fNear);

	memset(&m_mProjMatrix, 0, sizeof(OyMatrix));
	m_mProjMatrix._11 = w;
	m_mProjMatrix._22 = h;
	m_mProjMatrix._33 = Q;
	m_mProjMatrix._34 = 1.0f;
	m_mProjMatrix._43 = -Q*m_fNear;

	return OY_OK;
}

HRESULT OyD3D9Camera::CalcOrthoProjMatrix(float l, float r, float b, float t, float fN, float fF)
{
	if(fabs(fF - fN) < 0.01f)
		return OY_FAIL;

	float x =  2.0f / (r-l);
	float y =  2.0f / (t-b);
	float z =  1.0f / (fF-fN);
	float tx = -(r+l) / (r-l);
	float ty = -(t+b) / (t-b);
	float tz = -(fN) / (fF-fN);

	memset(&m_mProjMatrix, 0, sizeof(OyMatrix));
	m_mProjMatrix._11 = x;
	m_mProjMatrix._22 = y;
	m_mProjMatrix._33 = z;

	m_mProjMatrix._41 = tx;
	m_mProjMatrix._42 = ty;
	m_mProjMatrix._43 = tz;
	m_mProjMatrix._44 = 1.0f;

	return OY_OK;
}

HRESULT OyD3D9Camera::CalcOrthoProjMatrix(float w, float h, float fN, float fF)
{
	if(fabs(fF - fN) < 0.01f)
		return OY_FAIL;

	memset(&m_mProjMatrix, 0, sizeof(OyMatrix));

	m_mProjMatrix._11 = 2.0f / w;
	m_mProjMatrix._22 = 2.0f / h;
	m_mProjMatrix._33 = 1.0f / (fF - fN);
	
	m_mProjMatrix._43 = fN / (fN - fF);
	m_mProjMatrix._44 = 1.0f;

	return OY_OK;
}
